// DMPluginServices.h

#pragma once

#include "dmalgointerfaces.h"
#include "dmerrorhandling.h"
//#include <atlcoll.h>
//#include <vcclr.h>

using namespace System;
using namespace System::Reflection;
using namespace System::Collections;

namespace Microsoft
{
namespace SqlServer
{
namespace DataMining
{
namespace PluginAlgorithms
{

	ref class TaskProgressNotification;
	ref class PMMLWriter;
	ref class AlgorithmMetadataBase;

    
	public   ref class ContextServices sealed 
    {
    
    public:
        
		[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
		property int LCID
        {

			int get()
			{

				USES_ERROR_HANDLING;
				IDMContextServices* pCtx = (IDMContextServices*)::TlsGetValue(CONTEXT_TLS_SLOT);
	            if( ! pCtx )
	            {
	                throw gcnew System::InvalidOperationException();
	            }

	            DWORD   lcid = 0;
	            THROW_ON_FAILURE(pCtx->GetContextLocaleID( &lcid ) );
	            return lcid;
			}
        }
    
        void	CheckCancelled()
        {

			USES_ERROR_HANDLING;

			IDMContextServices* pCtx = (IDMContextServices*)::TlsGetValue(CONTEXT_TLS_SLOT);
            if( ! pCtx )
            {
                throw gcnew System::InvalidOperationException();
            }

			THROW_ON_FAILURE( SUCCEEDED(pCtx->CheckCancelled() ) );
        }

		property bool	ExecuteForPrepare
		{
			bool get()
			{
				return m_fExecuteForPrepare;
			}

		internal:
			void set(bool value)
			{
				m_fExecuteForPrepare = value;
			}

		}
    internal:
		ContextServices()
		{
			m_fExecuteForPrepare = false;
		}



    private:
		bool	m_fExecuteForPrepare;
    };


    
    class AutoPushContext
    {
    protected:
    public:
        AutoPushContext( IDMContextServices*    wpSvc)
        {
			DWORD ctxRefCount	=	(DWORD)::TlsGetValue( CONTEXTREF_TLS_SLOT );
			if( ctxRefCount == 0 )
			{
				::TlsSetValue( CONTEXT_TLS_SLOT, (LPVOID)wpSvc);
			}
			ctxRefCount++;
			::TlsSetValue(CONTEXTREF_TLS_SLOT, (LPVOID)ctxRefCount);
        }
        
        ~AutoPushContext()
        {

			DWORD ctxRefCount	=	(DWORD)::TlsGetValue( CONTEXTREF_TLS_SLOT );
			ctxRefCount--;
			::TlsSetValue( CONTEXTREF_TLS_SLOT, (LPVOID)ctxRefCount);

			if( ctxRefCount == 0 )
			{
				GC::Collect();
				GC::WaitForPendingFinalizers();
				::TlsSetValue( CONTEXT_TLS_SLOT, (LPVOID)NULL);
			}
        }
    };

	class AutoUseContext
	{
	protected:
		IDMContextServices*	m_wpSvc;
	public:
		AutoUseContext()
        {
			m_wpSvc	=	(IDMContextServices*)::TlsGetValue( CONTEXT_TLS_SLOT );
			if( m_wpSvc == NULL )
			{
				throw gcnew System::InvalidOperationException();
			}
        }
        
        ~AutoUseContext()
        {
			m_wpSvc = NULL;
        }

		IDMContextServices* operator ->()
		{
			return m_wpSvc;
		}

		operator IDMContextServices*()
		{
			return m_wpSvc;
		}


	};

    public   ref class ModelServices sealed 
    {
		private:

			AlgorithmMetadataBase^	m_metadata;
		internal:
			IDMModelServices*		m_wpSvc;
			ModelServices( IDMModelServices* wpSvc, AlgorithmMetadataBase^	metadata)
			{
				m_wpSvc		=	wpSvc;
				m_metadata	=	metadata;
			}
		
			
		public:

		

		[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
		property int LCID
		{
			int get()
			{
				HRESULT hRet;
				if( ! m_wpSvc )
				{
					throw gcnew System::InvalidOperationException();
				}

				DWORD   lcid = 0;
				hRet = m_wpSvc->GetModelLocaleID( &lcid );
				if( FAILED(hRet) )
					throw gcnew PluginAlgorithms::DMEngineException(hRet);
				return lcid;
			}
		}

        
        

		property System::String ^ Name
		{
			System::String^ get()
			{
				USES_ERROR_HANDLING;
				if( ! m_wpSvc )
				{
					throw gcnew System::InvalidOperationException();
				}

				const wchar_t*	lpwszModelname;
				THROW_ON_FAILURE(m_wpSvc->GetModelName( &lpwszModelname ) );
				return gcnew System::String(lpwszModelname);
			}
		}

		property AlgorithmMetadataBase^	Metadata
		{
			AlgorithmMetadataBase^	get()
			{
				return m_metadata;
			}
		}

		void	EmitSingleTraceNotification( System::String^ text);
		void	EmitSingleTraceNotification( System::String^ stringFormat, System::Int64 currentValue, System::Int64 totalValue);
		
		TaskProgressNotification^ CreateTaskNotification();


		// methods for rendering PMML content
		[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
		void RenderModelStatisticsPMML(PMMLWriter^	writer);
		[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
		void RenderMiningSchemaPMML(PMMLWriter^	writer);
		[System::Diagnostics::CodeAnalysis::SuppressMessage("Microsoft.Naming", "CA1705:LongAcronymsShouldBePascalCased")]
		void RenderModelCreationFlagsPMML(PMMLWriter^	writer);


		// method for accessing updated node captions
		String^		FindNodeCaption(String^ nodeUniqueName);
        
    internal:
        void	FireProgressNotification(DM_PROGRESS_NOTIFICATION_EVENT& in_event);
		void	FireProgressNotification( TaskProgressNotification^ taskProgress, DM_PROGRESS_NOTIFICATION_ENUM notifCode, bool bSuccess);
    };

	

	///////////////////////////////////////////////////////////
	// TaskProgressNotification -- Wraps a task event notification
	public ref class TaskProgressNotification sealed
	{
	internal:
		TaskProgressNotification(ModelServices^ in_modelServices);
		
		// To be preserved between calls
		System::Int64				m_jobID;
		System::Int64				m_timeStart;
		
	
	private:
		int									currentValue;
		int									totalValue;
		System::String^						notificationFormat;
		ModelServices^						modelServices;

		
	public:

		property int Current
		{
			int get()
			{
				return currentValue;
			}
			void set(int value)
			{
				currentValue = value;
			}
		}

		property int Total
		{
			int get()
			{
				return totalValue;
			}
			void set(int value)
			{
				totalValue = value;
			}
		}

		property String^ Format
		{
			String^	get()
			{
				return notificationFormat;
			}
			void set(String^ value)
			{
				notificationFormat = value;
			}
		}

		void Start();
		void Progress();
		void End(bool success);
	};
}
}
}
}
